package com.atwisdom.star.common.util;

import com.googlecode.aviator.AviatorEvaluator;
import lombok.Data;
import net.sf.json.JSONArray;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.DoubleSummaryStatistics;
import java.util.Map;

/**
 * @program: starCloudServerFramework
 * @author: panhao
 * @create: 2022-03-18 15:22
 */
@Data
public class StarFormula {
    /**
    * @Description: fnList为所有可调用的自定义函数
    */
    private final String[] fnList={"sum","add"};
    private String expression;
    private Map<String,Object> params;

    public StarFormula(String expression, Map<String, Object> params) {
        this.expression = expression;
        this.params = params;
    }
    /**
    * @Description: execute，执行方法
    * @Param: * @param expression
    * @param params
    * @return: java.lang.Double
    * @Author: panhao
    * @Date: 3:07 2022/3/20
    */
    public static Double execute(String expression, Map<String, Object> params){
        StarFormula star=new StarFormula(expression,params);
        String ex=star.findAllFunction(expression);
        return (Double) star.run(ex);
    }
    /**
    * @Description: findAllFunction,替换表达式中所有函数
    * @Param: * @param expression
    * @return: java.lang.String
    * @Author: panhao
    * @Date: 3:07 2022/3/20
    */
    public String findAllFunction(String expression){
        for(String str:fnList){
            expression=findFunction(str,expression);
        }
        return expression;
    }
    /**
    * @Description: findFunction，替换表达式中同一个函数，并且计算函数值
    * @Param: * @param fnName
    * @param expression
    * @return: java.lang.String
    * @Author: panhao
    * @Date: 3:05 2022/3/20
    */
    private String findFunction(String fnName,String expression){
        int pos=expression.indexOf(fnName+"(");
        if(pos>=0){
            String fnStr=getFunctionStr(fnName,expression.substring(pos));
            String prefix=expression.substring(0,expression.indexOf(fnStr));
            String affix=expression.substring((prefix+fnStr).length());
            expression=prefix+handleFunction(fnName,fnStr)+affix;
            expression=findFunction(fnName,expression);
        }
        return expression;
    }
    /**
    * @Description: handleFunction，计算函数值，其中调用自定义函数
    * @Param: * @param fnName
    * @param fnStr
    * @return: java.lang.Double
    * @Author: panhao
    * @Date: 3:04 2022/3/20
    */
    private Double handleFunction(String fnName,String fnStr){
        String fnArgs=fnStr.substring(fnName.length()+1,fnStr.length()-1);
        try {
            Method m=this.getClass().getDeclaredMethod(fnName,String.class);
            Double result = (Double) m.invoke(this,findAllFunction(fnArgs));
            return result;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
    * @Description: getFunctionStr，获取函数的完整字符串
    * @Param: * @param fnName
    * @param str
    * @return: java.lang.String
    * @Author: panhao
    * @Date: 3:03 2022/3/20
    */
    private String getFunctionStr(String fnName,String str){
        return fnName+getBracketsStr(str.substring(fnName.length()));
    }
    /**
    * @Description: getBracketsStr，截取（）中完整字符串，包括括号
    * @Param: * @param whole
    * @return: java.lang.String
    * @Author: panhao
    * @Date: 3:02 2022/3/20
    */
    private String getBracketsStr(String whole){
        int startPos=whole.indexOf("(");
        if(startPos==-1){
            throw new RuntimeException("没有发现（");
        }
        String str=whole.substring(startPos+1);
        int markCount=0;
        int endPos=0;
        char[] ch=str.toCharArray();
        for(int i=0;i<ch.length;i++){
            if(ch[i]=='('){
                markCount++;
            }else if(ch[i]==')'){
                if(markCount>0){
                    markCount--;
                }else{
                    endPos=i;
                    break;
                }
            }
        };
        if(endPos==0){
            throw new RuntimeException("无效方法");
        }
        return whole.substring(startPos,startPos+endPos+2);
    }
    /**
    * @Description: run,执行简单的计算
    * @Param: * @param expression
    * @return: java.lang.Object
    * @Author: panhao
    * @Date: 3:00 2022/3/20
    */
    private Object run(String expression){
        Object result= AviatorEvaluator.execute(changeVar(expression));
        return result;
    }
    /**
    * @Description: changeVar,表达式中变量为[x],参数表中为x；将参数表中的值替换表达式中的[x]
    * @Param: * @param expression
    * @return: java.lang.String
    * @Author: panhao
    * @Date: 2:58 2022/3/20
    */
    private String changeVar(String expression){
        for (String var : params.keySet()) {
            expression=expression.replaceAll("\\<#"+var+"\\#>",""+params.get(var));
        }
        return expression;
    }
    /**
    * ****************************************************************
    * 添加计算函数
    * ****************************************************************
    */
    /**
    * @Description: add,引用格式为:add([a],[b])
    * @Param: * @param args
    * @return: java.lang.Double
    * @Author: panhao
    * @Date: 1:35 2022/3/20
    */
    public Double add(String args){
        String[] varStr=changeVar(args).split(",");
        return Double.parseDouble(varStr[0])+Double.parseDouble(varStr[1]) ;
    }
    /**
    * @Description: sum,引用格式为:sum([w]),w=[1,2,3]
    * @Param: * @param args
    * @return: java.lang.Double
    * @Author: panhao
    * @Date: 2:57 2022/3/20
    */
    public Double sum(String args){
        return getSummaryStatistics(args).getSum();
    }

    public Double average(String args){
        return getSummaryStatistics(args).getAverage();
    }

    public Double max(String args){
        return getSummaryStatistics(args).getMax();
    }

    public Double min(String args){
        return getSummaryStatistics(args).getMin();
    }

    public Double count(String args){
        return Double.valueOf(getSummaryStatistics(args).getCount());
    }

    public DoubleSummaryStatistics getSummaryStatistics(String args) {
        JSONArray varList= JSONArray.fromObject(changeVar(args));
        return varList.stream().mapToDouble((x) -> Double.valueOf(x.toString())).summaryStatistics();
    }

}